package com.lotso.web.module.batch.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.lotso.web.common.ReqParam;
import com.lotso.web.common.Result;
import com.lotso.web.common.exception.BusinessException;
import com.lotso.web.common.utils.DateUtil;
import com.lotso.web.module.batch.entity.BatchSchedule;
import com.lotso.web.module.batch.entity.BatchScheduleParam;
import com.lotso.web.module.batch.mapper.IBatchScheduleMapper;
import com.lotso.web.module.batch.mapper.IBatchScheduleParamMapper;
import com.lotso.web.module.batch.model.ScheduleStatus;
import com.lotso.web.module.batch.service.IBatchScheduleService;
import com.lotso.web.module.batch.service.QuartzScheduleEngine;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * ClassName: ScheduleJobServiceImpl
 * Description:
 * Date: 2018/9/10 13:42 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class BatchScheduleServiceImpl extends ServiceImpl<IBatchScheduleMapper, BatchSchedule> implements IBatchScheduleService {

    @Autowired
    private IBatchScheduleMapper scheduleMapper;
    @Autowired
    private IBatchScheduleParamMapper scheduleParamMapper;
    @Autowired
    private QuartzScheduleEngine scheduleEngine;

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public Result<BatchSchedule> list(ReqParam reqParam) {
        Page<BatchSchedule> jobPage = new Page<>(reqParam.getPage(), reqParam.getLimit());
        List<BatchSchedule> jobList = scheduleMapper.listByPage(jobPage, reqParam);
        return new Result<>(jobPage.getTotal(), jobList);
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<BatchSchedule> list() {
        Wrapper<BatchSchedule> wrapper = new EntityWrapper<>();
        return scheduleMapper.selectList(wrapper.orderBy("createDate", true));
    }

    @Override
    public boolean add(BatchSchedule batchSchedule) {
        batchSchedule.buildCronExpression().initCode();
        boolean res = this.insert(batchSchedule);

        if (res) {
            // 保存依赖计划
            List<BatchSchedule> dependencies = batchSchedule.getDependencies();
            if (Objects.nonNull(dependencies) && !dependencies.isEmpty()) {
                scheduleMapper.insertBatchDependencies(batchSchedule.getId(), dependencies);
            }

            // 保存参数
            List<BatchScheduleParam> params = batchSchedule.buildParams().getParams();
            if (Objects.nonNull(params) && !params.isEmpty()) {
                scheduleMapper.insertBatchParams(batchSchedule.getId(), batchSchedule.getCode(), params);
            }
        }
        return res;
    }

    @Override
    public boolean update(BatchSchedule batchSchedule) {

        String oldCron = batchSchedule.getCronExpression();
        batchSchedule = batchSchedule.buildCronExpression();
        String cronExpression = batchSchedule.getCronExpression();
        // 修改了执行表达式
        if (!Objects.equals(oldCron, cronExpression)) {
            try {
                scheduleEngine.updateJobCronExpression(batchSchedule);
            } catch (SchedulerException e) {
                throw new BusinessException("任务计划失败: " + e.getMessage());
            }
        }
        batchSchedule.setUpdateDate(DateUtil.now());
        boolean res = scheduleMapper.updateById(batchSchedule) > 0;

        if (res) {
            String scheduleId = batchSchedule.getId();
            // 修改任务依赖
            scheduleMapper.deleteDependencies(scheduleId);
            List<BatchSchedule> dependencies = batchSchedule.getDependencies();
            if (Objects.nonNull(dependencies) && !dependencies.isEmpty()) {
                scheduleMapper.insertBatchDependencies(batchSchedule.getId(), dependencies);
            }

            // 修改参数
            scheduleParamMapper.delete(new EntityWrapper<BatchScheduleParam>().eq("scheduleId", scheduleId));
            List<BatchScheduleParam> params = batchSchedule.buildParams().getParams();
            if (Objects.nonNull(params) && !params.isEmpty()) {
                scheduleMapper.insertBatchParams(scheduleId, batchSchedule.getCode(), params);
            }
        }
        return res;
    }

    @Override
    public boolean delete(String scheduleId) {
        BatchSchedule batchSchedule = scheduleMapper.findById(scheduleId);
        int status = batchSchedule.getStatus();
        if (ScheduleStatus.RUNNING == status) {
            throw new BusinessException("任务计划出于不能删除的状态，请确认");
        }
        boolean res;
        try {
            res = scheduleEngine.deleteJob(batchSchedule);
        } catch (SchedulerException e) {
            throw new BusinessException("删除任务计划失败" + e.getMessage());
        }
        if (res) {
            res = scheduleMapper.deleteById(scheduleId) > 0;
            if (res) {
                Wrapper<BatchScheduleParam> wrapper = new EntityWrapper<BatchScheduleParam>().eq("scheduleId", batchSchedule.getId());
                if (scheduleParamMapper.delete(wrapper) < 0) {
                    throw new BusinessException("删除任务计划参数失败，请重试");
                }

                if (scheduleMapper.deleteDependencies(scheduleId) < 0) {
                    throw new BusinessException("删除任务计划依赖失败，请重试");
                }
            }
        }
        return res;
    }

    @Override
    public boolean run(String scheduleId) {
        BatchSchedule batchSchedule = scheduleMapper.findById(scheduleId);
        List<BatchSchedule> dependencies = batchSchedule.getDependencies();
        if (!dependencies.isEmpty()) {
            throw new BusinessException("任务计划启动失败: 已配置依赖计划，系统将自动调用，不能人为启动");
        }

        Date date;
        try {
            date = scheduleEngine.addJob(batchSchedule);
        } catch (Exception e) {
            throw new BusinessException("任务计划启动失败: " + e.getMessage());
        }
        batchSchedule = new BatchSchedule();
        batchSchedule.setId(scheduleId);
        batchSchedule.setStartDate(date);
        batchSchedule.setUpdateDate(date);
        batchSchedule.setStatus(ScheduleStatus.NORMAL);
        return scheduleMapper.updateById(batchSchedule) > 0;
    }

    @Override
    public boolean pause(String scheduleId) {
        BatchSchedule batchSchedule = scheduleMapper.findById(scheduleId);
        int status = batchSchedule.getStatus();
        if (ScheduleStatus.NORMAL != status && ScheduleStatus.RUNNING != status
                && ScheduleStatus.COMPLETE != status) {
            throw new BusinessException("任务计划出于不能暂停的状态，请确认");
        }

        try {
            scheduleEngine.pauseJob(batchSchedule);
        } catch (SchedulerException e) {
            throw new BusinessException("任务计划暂停失败: " + e.getMessage());
        }

        batchSchedule = new BatchSchedule();
        batchSchedule.setId(scheduleId);
        batchSchedule.setEndDate(DateUtil.now());
        batchSchedule.setUpdateDate(DateUtil.now());
        batchSchedule.setStatus(ScheduleStatus.PAUSED);
        return scheduleMapper.updateById(batchSchedule) > 0;
    }

    @Override
    public boolean resume(String scheduleId) {
        BatchSchedule batchSchedule = scheduleMapper.findById(scheduleId);
        int status = batchSchedule.getStatus();
        if (ScheduleStatus.PAUSED != status) {
            throw new BusinessException("任务计划不在暂停的状态，请确认");
        }

        try {
            scheduleEngine.resumeJob(batchSchedule);
        } catch (SchedulerException e) {
            throw new BusinessException("任务计划恢复运行失败: " + e.getMessage());
        }

        batchSchedule = new BatchSchedule();
        batchSchedule.setId(scheduleId);
        batchSchedule.setUpdateDate(DateUtil.now());
        batchSchedule.setStatus(ScheduleStatus.NORMAL);
        return scheduleMapper.updateById(batchSchedule) > 0;
    }

    @Override
    public void notifyChildren(BatchSchedule scheduleJob) {
        String id = scheduleJob.getId();
        List<BatchSchedule> childrenSchedule = scheduleMapper.findChildrenSchedule(id);
        if (Objects.nonNull(childrenSchedule) && !childrenSchedule.isEmpty()) {
            for (BatchSchedule childSchedule : childrenSchedule) {
                Date date;
                try {
                    date = scheduleEngine.addJob(childSchedule);
                } catch (Exception e) {
                    throw new BusinessException("子任务计划唤醒失败: " + e.getMessage());
                }
                BatchSchedule schedule = new BatchSchedule();
                schedule.setId(childSchedule.getId());
                schedule.setStartDate(date);
                schedule.setUpdateDate(date);
                schedule.setStatus(ScheduleStatus.NORMAL);
                if (!this.updateById(schedule)) {
                    throw new BusinessException("子任务计划唤醒失败，更新子任务计划状态异常");
                }
            }
        }
    }
}
